/**
 * Copyright (c) 2018-2022, NXOS Development Team
 * SPDX-License-Identifier: Apache-2.0
 *
 * Contains: GPIO driver
 *
 * Change Logs:
 * Date           Author            Notes
 * 2023-1-4       planck           Init
 */

#include <nxos.h>

#include <drivers/gpio.h>
#include <drivers/pin.h>
#include <regs.h>
#include <base/log.h>
#include <base/driver.h>
#include <base/string.h>
#include <base/ioremap.h>

// d1s have 6 group gpio
// 6 groups of ports (PB(6), PC(6), PD(23), PE(14), PF(7), PG(16))
//  PB2 - PB7
//  PC2 - PC7
//  PD0 - PD22
//  PE0 - PE13
//  PF0 - PF6
//  PG0 - PG15

#define DRV_NAME "gpio driver"
#define DEV_NAME "gpio"

void d1_set_gpio_pull(NX_U32 gpio_port, NX_U32 gpio_pin, NX_U16 pull)
{
    NX_U32 pin_level = 0;
    NX_U32 gpio_base_addr = 0;
    NX_U32 val = 0;
    pin_level = gpio_pin / 16;
    gpio_base_addr = gpio_port + pin_level * 0x04 + 0xE4;
    val = Read32(gpio_base_addr);

    val &= ~(0x3 << ((gpio_pin & 0xf) << 1));
    val |= ((pull & 0x3) << ((gpio_pin & 0x3) << 1));

    Write32(gpio_base_addr, val);
}

void d1_set_gpio_mode(NX_U32 gpio_port, NX_U32 gpio_pin, NX_U16 mode)
{
    NX_U32 pin_level = 0;
    NX_U32 gpio_base_addr = 0;
    NX_U32 val = 0;
    pin_level = gpio_pin / 8;
    gpio_base_addr = gpio_port + pin_level * 0x04;

    val = Read32(gpio_base_addr);

    val &= ~(0xf << ((gpio_pin & 0x7) << 2));
    val |= ((mode & 0xf) << ((gpio_pin & 0x7) << 2));

    Write32(gpio_base_addr, val);
}

void d1_set_gpio_val(NX_U32 gpio_port, NX_U32 gpio_pin, NX_U32 val)
{
    NX_U32 gpio_base_addr = 0;
    NX_U32 cur_val = 0;
    gpio_base_addr = gpio_port + 0x10;
    cur_val = Read32(gpio_base_addr);

    if (val)
    {
        cur_val |= (1 << gpio_pin);
    }
    else
    {
        cur_val &= ~(1 << gpio_pin);
    }

    Write32(gpio_base_addr, cur_val);
}

NX_U8 d1_get_gpio_val(NX_U32 gpio_port, NX_U32 gpio_pin)
{
    NX_U32 gpio_base_addr = 0;
    NX_U32 cur_val = 0;
    gpio_base_addr = gpio_port + 0x10;
    cur_val = Read32(gpio_base_addr);
    NX_U8 ret = 0;

    if (cur_val & (1 << gpio_pin))
    {
        ret = 1;
    }
    else
    {
        ret = 0;
    }

    return ret;
}

void d1_set_gpio_irq_enable(NX_U32 gpio_port, NX_U32 gpio_pin, NX_U32 cfg, NX_U8 enable)
{
    NX_U32 pin_level = 0;
    NX_U32 gpio_base_addr = 0;
    NX_U32 val = 0;
    pin_level = gpio_pin / 8;
    gpio_base_addr = F133_GPIO_BASE + D1_GPIO_PB_EINT_CFG0 + pin_level * 0x04;
    val = Read32(gpio_base_addr);

    val &= ~(0xf << ((gpio_pin & 0x7) << 2));
    val |= ((cfg & 0x0f) << ((gpio_pin & 0x7) << 2));

    Write32(gpio_base_addr, val);

    // set gpio irq enable
    gpio_base_addr = F133_GPIO_BASE + D1_GPIO_PB_EINT_CTL;
    val = Read32(gpio_base_addr);
    if (enable)
    {
        val |= (1 << gpio_pin);
    }
    else
    {
        val &= ~(1 << gpio_pin);
    }
    Write32(gpio_base_addr, val);
}

NX_U32 d1_get_pb_irq_status(void)
{
    NX_U32 val = 0;
    NX_U32 gpio_base_addr = 0;
    gpio_base_addr = F133_GPIO_BASE + D1_GPIO_PB_EINT_STATUS;
    val = Read32(gpio_base_addr);

    Write32(gpio_base_addr, val); // write 1 clear
    return val;
}

void get_gpio_from_pin(NX_UArch pin, NX_U32 *cur_gpio_port, NX_U32 *cur_gpio_pin)
{
    if (pin < 6) // PB2 - PB7
    {
        *cur_gpio_port = GPIO_PORT_B;
        *cur_gpio_pin = pin + 2;
    }
    else if (pin < 12) // PC2 - PC7
    {
        *cur_gpio_port = GPIO_PORT_C;
        *cur_gpio_pin = pin - 6 + 2;
    }
    else if (pin < 35) // PD0 - PD22
    {
        *cur_gpio_port = GPIO_PORT_D;
        *cur_gpio_pin = pin - 12;
    }
    else if (pin < 49) // PE0 - PE13
    {
        *cur_gpio_port = GPIO_PORT_E;
        *cur_gpio_pin = pin - 35;
    }
    else if (pin < 56) // PF0 - PF6
    {
        *cur_gpio_port = GPIO_PORT_F;
        *cur_gpio_pin = pin - 49;
    }
    else if (pin < 72) // PG0 - PG15
    {
        *cur_gpio_port = GPIO_PORT_G;
        *cur_gpio_pin = pin - 56;
    }
}

void d1s_pin_mode(NX_UArch pin, NX_UArch mode)
{
    GPIO_FUNC d1s_mode = OUTPUT;
    NX_U32 cur_gpio_port = 0;
    NX_U32 cur_gpio_pin = 0;

    get_gpio_from_pin(pin, &cur_gpio_port, &cur_gpio_pin);

    switch (mode)
    {
    case PIN_MODE_OUTPUT:
        d1s_mode = OUTPUT;
        break;
    case PIN_MODE_INPUT:
        d1s_mode = INPUT;
        break;
    case PIN_MODE_INPUT_PULLUP: // to do
        d1s_mode = INPUT;
        break;
    case PIN_MODE_INPUT_PULLDOWN: // to do
        d1s_mode = INPUT;
        break;
    case PIN_MODE_OUTPUT_OD:
        d1s_mode = OUTPUT;
        break;
    }

    d1_set_gpio_mode(cur_gpio_port, cur_gpio_pin, d1s_mode);
}

void d1s_pin_write(NX_UArch pin, NX_UArch value)
{
    NX_U32 cur_gpio_port = 0;
    NX_U32 cur_gpio_pin = 0;

    get_gpio_from_pin(pin, &cur_gpio_port, &cur_gpio_pin);

    d1_set_gpio_val(cur_gpio_port, cur_gpio_pin, value);
}

int d1s_pin_read(NX_UArch pin)
{
    NX_U32 pin_level = 0;

    NX_U32 cur_gpio_port = 0;
    NX_U32 cur_gpio_pin = 0;

    get_gpio_from_pin(pin, &cur_gpio_port, &cur_gpio_pin);

    pin_level = d1_get_gpio_val(cur_gpio_port, cur_gpio_pin);
    return pin_level;
}

NX_Error d1s_pin_attach_irq(NX_I32 pin, NX_U32 mode, void (*hdr)(void *args), void *args)
{
    return NX_EOK;
}

NX_Error d1s_pin_detach_irq(NX_I32 pin)
{
    return NX_EOK;
}

NX_Error d1s_pin_irq_enable(NX_UArch pin, NX_U32 enabled)
{
    return NX_EOK;
}

NX_PRIVATE NX_Error Pin_Control(struct NX_Device *device, NX_U32 cmd, void *arg)
{

    NX_UArch pin = 0;
    int hw_port_num, hw_pin_num = 0;
    int i, name_len;

    switch (cmd)
    {
    case NX_PIN_CMD_GETPIN:
        name_len = NX_StrLen(device->name);

        if ((name_len < 4) || (name_len >= 6))
        {
            return -NX_EINVAL;
        }
        if ((device->name[0] != 'P') || (device->name[2] != '.'))
        {
            return -NX_EINVAL;
        }

        if (device->name[1] == 'B')
        {
            pin = 0;
        }
        else if (device->name[1] == 'C')
        {
            pin = 6;
        }
        else if (device->name[1] == 'D')
        {
            pin = 12;
        }
        else if (device->name[1] == 'E')
        {
            pin = 35;
        }
        else if (device->name[1] == 'F')
        {
            pin = 49;
        }
        else if (device->name[1] == 'G')
        {
            pin = 56;
        }

        for (i = 3; i < name_len; i++)
        {
            hw_pin_num *= 10;
            hw_pin_num += device->name[i] - '0';
        }

        if (hw_pin_num > 23)
        {
            return -NX_EINVAL;
        }
        arg = pin + hw_pin_num;
        break;
    default:
        return NX_EINVAL;
    }
    return NX_EOK;
}


NX_IArch d1s_pin_get(const char *name)
{

    NX_IArch pin = 0;
    int hw_port_num, hw_pin_num = 0;
    int i, name_len;

    name_len = NX_StrLen(name);

    if ((name_len < 4) || (name_len >= 6))
    {
        return -NX_EINVAL;
    }
    if ((name[0] != 'P') || (name[2] != '.'))
    {
        return -NX_EINVAL;
    }

    if(name[1] == 'B')
    {
        pin = 0;
    }
    else if(name[1] == 'C')
    {
        pin = 6;
    }
    else if(name[1] == 'D')
    {
        pin = 12;
    }
    else if(name[1] == 'E')
    {
        pin = 35;
    }
    else if(name[1] == 'F')
    {
        pin = 49;
    }
    else if(name[1] == 'G')
    {
        pin = 56;
    }

    for (i = 3; i < name_len; i++)
    {
        hw_pin_num *= 10;
        hw_pin_num += name[i] - '0';
    }

    if(hw_pin_num > 23)
    {
        return -NX_EINVAL;
    }

    return pin + hw_pin_num;
}

NX_PRIVATE NX_Error Pin_Write(struct NX_Device *device, void *buf, NX_Offset off, NX_Size len, NX_Size *outLen)
{
    d1s_pin_write(off, &buf);
    return NX_EOK;
}

NX_PRIVATE NX_Error Pin_Read(struct NX_Device *device, void *value, NX_Offset off, NX_Size len, NX_Size *outLen)
{
    value = (void *)d1s_pin_read(off);
    if (value == NX_NULL)
    {
        return NX_ERROR;
    }
    return NX_EOK;
}

NX_PRIVATE NX_DriverOps PIN_DriverOps =
    {
        .write = Pin_Write,
        .read = Pin_Read,
        .control = Pin_Control,
};

int GPIO_DriverInit(void)
{
    NX_Device *device;
    NX_Driver *driver = NX_DriverCreate(DRV_NAME, NX_DEVICE_TYPE_CHAR, 0, &PIN_DriverOps);

    if (driver == NX_NULL)
    {
        NX_LOG_E("create driver failed!");
        return;
    }

    if (NX_DriverAttachDevice(driver, DEV_NAME, &device) != NX_EOK)
    {
        NX_LOG_E("attach device %s failed!", DEV_NAME);
        NX_DriverDestroy(driver);
        return;
    }

    if (NX_DriverRegister(driver) != NX_EOK)
    {
        NX_LOG_E("register driver %s failed!", DRV_NAME);
        NX_DriverDetachDevice(driver, DEV_NAME);
        NX_DriverDestroy(driver);
        return;
    }
}

NX_PRIVATE void GPIO_DriverExit(void)
{
    NX_DriverCleanup(DRV_NAME);
}

NX_DRV_INIT(GPIO_DriverInit);
NX_DRV_EXIT(GPIO_DriverExit);